/*
 * ========================================================================
 *
 *       Filename:  studyboard.cpp
 *
 *    Description:  This implements the studyboard.
 *
 *        Version:  1.0
 *        Created:  05/11/2010 11:27:47 PM
 *       Revision:  none
 *       Compiler:  g++
 *
 *         Author:  Alex Avance (aa), aravance@gmail.com
 *        Company:  independent
 *
 * ========================================================================
 *
 * Copyright (C) 2010 Alex Avance
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-
 * 1307, USA.
 *
 * ========================================================================
 */
#include "studyboard.h"
#include "wx/msgdlg.h"
#include "wx/sizer.h"

BEGIN_EVENT_TABLE( StudyBoardPanel, wxPanel )
  EVT_LISTBOX( ID_StudyList, StudyBoardPanel::OnSelect )
  EVT_LISTBOX_DCLICK( ID_StudyList, StudyBoardPanel::OnDoubleClick )
  EVT_BUTTON( ID_StudyNext, StudyBoardPanel::OnNext )
  EVT_BUTTON( ID_StudyPrev, StudyBoardPanel::OnPrev )
  EVT_BUTTON( ID_StudySort, StudyBoardPanel::OnSort )
  EVT_BUTTON( ID_StudyShuf, StudyBoardPanel::OnShuffle )
END_EVENT_TABLE()

/**************************************************************************
Class Name: StudyBoardPanel
Method Name: StudyBoardPanel
Description: constructor
**************************************************************************/
StudyBoardPanel::StudyBoardPanel ( wxFrame *parent, StudyDeck &deck )
  : wxPanel( parent, wxID_ANY ), m_Parent( parent ), m_Deck( deck )
{
  // ==================== Main Panel    ==================================================
  wxBoxSizer *sizerParent= new wxBoxSizer( wxHORIZONTAL );
  wxBoxSizer *sizerRight= new wxBoxSizer( wxVERTICAL );
  wxBoxSizer *sizerLeft= new wxBoxSizer( wxHORIZONTAL );

  // ==================== List Box      ==================================================
  m_ListBox= new wxListBox( this, ID_StudyList, wxDefaultPosition, wxDefaultSize,
                            0, NULL, wxLB_SINGLE | wxLB_NEEDED_SB );
  sizerLeft->Add( m_ListBox, 1, wxEXPAND | wxALL, 5 );

  // ==================== Text Fields   ==================================================
  m_TextFront= new CustomTextCtrl( this, ID_StudyFront, _(""), wxDefaultPosition, wxDefaultSize,
                                   wxTE_MULTILINE, wxDefaultValidator, _("Front") );
  sizerRight->Add( m_TextFront, 1, wxEXPAND | wxALL, 5 );

  m_TextBack= new CustomTextCtrl( this, ID_StudyBack, _(""), wxDefaultPosition, wxDefaultSize,
                                  wxTE_MULTILINE, wxDefaultValidator, _("Back") );
  sizerRight->Add( m_TextBack, 1, wxEXPAND | wxALL, 5 );

  // ==================== Buttons       ==================================================
  wxBoxSizer *sizerButtons= new wxBoxSizer( wxHORIZONTAL );

  sizerButtons->AddStretchSpacer( 1 );

  m_ButtonShuf= new wxButton( this, ID_StudyShuf, _("S&huffle") );
  m_ButtonSort= new wxButton( this, ID_StudySort, _("S&ort") );
  m_ButtonPrev= new wxButton( this, ID_StudyPrev, _("&Previous") );
  m_ButtonNext= new wxButton( this, ID_StudyNext, _("&Next") );

  sizerButtons->Add( m_ButtonShuf, 0, wxEXPAND | wxALL, 5 );
  sizerButtons->Add( m_ButtonSort, 0, wxEXPAND | wxALL, 5 );
  sizerButtons->Add( m_ButtonPrev, 0, wxEXPAND | wxALL, 5 );
  sizerButtons->Add( m_ButtonNext, 0, wxEXPAND | wxALL, 5 );

  sizerRight->Add( sizerButtons, 0, wxEXPAND | wxALL, 5 );

  // ==================== Sizers        ==================================================
  sizerParent->Add( sizerLeft, 2, wxEXPAND | wxALL, 5 );
  sizerParent->Add( sizerRight, 5, wxEXPAND | wxALL, 5 );

  SetSizer( sizerParent );

  Reset();

  m_Modified= false;
  m_CurrentSelection= 0;
} // ----- end of method StudyBoardPanel::StudyBoardPanel (constructor) -----

/**************************************************************************
Class Name: StudyBoardPanel
Method Name: StudyBoardPanel
Description: destructor
**************************************************************************/
StudyBoardPanel::~StudyBoardPanel ()
{} // ----- end of method StudyBoardPanel::StudyBoardPanel (destructor) -----

/**************************************************************************
Class: StudyBoardPanel
Method: IsModified
Description: Return the status of the modified flag.
**************************************************************************/
bool StudyBoardPanel::IsModified () const
{ return m_Modified; } // ----- end of method StudyBoardPanel::IsModified -----

/**************************************************************************
Class: StudyBoardPanel
Method: ConfirmSwitch
Description: Confirm the switching of the current panel.
**************************************************************************/
bool StudyBoardPanel::ConfirmSwitch ( bool force )
{
  int result= wxYES;

  if ( !force && ( m_TextFront->IsModified() || m_TextBack->IsModified() ) )
    result= ConfirmSwitchCard();

  if ( force || result != wxCANCEL )
    SelectCard( 0 );

  return ( result != wxCANCEL );
} // ----- end of method StudyBoardPanel::ConfirmSwitch -----

/**************************************************************************
Class: StudyBoardPanel
Method: ConfirmSwitchCard
Description: Ask about switching cards and return the result.
**************************************************************************/
int StudyBoardPanel::ConfirmSwitchCard ()
{
  int result= wxYES;

  if ( m_TextFront->IsModified() || m_TextBack->IsModified() )
  {
    result= wxMessageBox( _("The current card has changed, save first?"),
                          _("Card Changed"),
                          wxYES_NO | wxCANCEL | wxICON_QUESTION, this );

    if ( result == wxYES )
    {
      UpdateCardEntry( m_CurrentSelection );
      SaveCard( m_CurrentSelection );
    }
  }

  return result;
} // ----- end of method StudyBoardPanel::ConfirmSwitchCard -----

/**************************************************************************
Class: StudyBoardPanel
Method: Unmodified
Description: Reset the modified flag.
**************************************************************************/
void StudyBoardPanel::Unmodified ()
{ m_Modified= false; } // ----- end of method StudyBoardPanel::Unmodified -----

/**************************************************************************
Class: StudyBoardPanel
Method: Reset
Description: This resets the board.
**************************************************************************/
void StudyBoardPanel::Reset ()
{
  m_ListBox->Show();
  Update();
  SelectCard( 0 );
} // ----- end of method StudyBoardPanel::Reset -----

/**************************************************************************
Class: StudyBoardPanel
Method: Update
Description: Update the panel.
**************************************************************************/
void StudyBoardPanel::Update ()
{
  BuildList();
  UpdateButtons();

  if ( m_ListBox->GetSelection() == wxNOT_FOUND )
    SelectCard( 0 );

  m_ListBox->SetFocus();

  // problem when board switching, the text boxes lock until you select something
  // there's another copy of this in the Show(bool) function
  // the next two lines are a pretty hack <+fix this+>
  m_TextFront->SetEditable( true );
  m_TextBack->SetEditable( true );

  Layout();
} // ----- end of method StudyBoardPanel::Update -----

/**************************************************************************
Class: StudyBoardPanel
Method: UpdateButtons
Description: Update the buttons based on deck count.
**************************************************************************/
void StudyBoardPanel::UpdateButtons ()
{
  if ( m_Deck.Size() > 1 )
  {
    m_ButtonShuf->Enable();
    m_ButtonSort->Enable();
    m_ButtonPrev->Enable();
    m_ButtonNext->Enable();
  }
  else
  {
    m_ButtonShuf->Disable();
    m_ButtonSort->Disable();
    m_ButtonPrev->Disable();
    m_ButtonNext->Disable();
  }
} // ----- end of method StudyBoardPanel::UpdateButtons -----

/**************************************************************************
Class: StudyBoardPanel
Method: BuildList
Description: This fills the listbox with the list elements.
**************************************************************************/
void StudyBoardPanel::BuildList ()
{
  StudyDeck::iterator it;
  StudyDeck::iterator stop= m_Deck.end();

  m_ListBox->Clear();

  for ( it= m_Deck.begin(); it != stop; ++it )
    m_ListBox->Append( it->Front() );

  m_ListBox->Append( _("+ New Card") );
} // ----- end of method StudyBoardPanel::BuildList -----

/**************************************************************************
Class: StudyBoardPanel
Method: UpdateCardEntry
Description: Update the card list entry referring to a saved card.
**************************************************************************/
void StudyBoardPanel::UpdateCardEntry ( size_t pos )
{
  if ( pos == m_Deck.Count() )
    m_ListBox->Insert( m_TextFront->GetValue(), pos );
  else
    m_ListBox->SetString( pos, m_TextFront->GetValue() );
} // ----- end of method StudyBoardPanel::UpdateCardEntry -----

/**************************************************************************
Class: StudyBoardPanel
Method: SaveCard
Description: Save a card.
**************************************************************************/
void StudyBoardPanel::SaveCard ( size_t pos )
{
  if ( pos == (size_t)-1 )
    if ( ( pos= m_ListBox->GetSelection() ) == wxNOT_FOUND )
      throw "Error: couldn't determine save location";

  if ( pos >= m_Deck.Count() )
    m_Deck.Append( m_TextFront->GetValue(), m_TextBack->GetValue() );
  else
  {
    m_Deck[pos].SetFront( m_TextFront->GetValue() );
    m_Deck[pos].SetBack( m_TextBack->GetValue() );
  }

  m_TextFront->SetModified( false );
  m_TextBack->SetModified( false );

  m_Modified= true;
} // ----- end of method StudyBoardPanel::SaveCard -----

/**************************************************************************
Class: StudyBoardPanel
Method: DelCard
Description: Delete a card.
**************************************************************************/
void StudyBoardPanel::DelCard ( size_t pos )
{
  if ( pos == (size_t)-1 )
    pos= m_ListBox->GetSelection();

  if ( pos >= m_Deck.Count() )
    throw "Error: not that many cards, can't delete";
  else
  {
    m_Deck.Erase( pos );
    m_ListBox->Delete( pos );
  }

  m_Modified= true;
} // ----- end of method StudyBoardPanel::DelCard -----

/**************************************************************************
Class: StudyBoardPanel
Method: SelectCard
Description: 
**************************************************************************/
void StudyBoardPanel::SelectCard ( size_t pos )
{
  m_ListBox->SetSelection( pos );
  if ( pos < m_Deck.Size() )
  {
    m_TextFront->SetValue( m_Deck[pos].Front() );
    m_TextBack->SetValue( m_Deck[pos].Back() );
  }
  else
  {
    m_TextFront->SetValue( _("") );
    m_TextBack->SetValue( _("") );
  }

  m_CurrentSelection= m_ListBox->GetSelection();
} // ----- end of method StudyBoardPanel::SelectCard -----

/**************************************************************************
Class: StudyBoardPanel
Method: Show
Description: Handle showing of the panel.
**************************************************************************/
bool StudyBoardPanel::Show ( bool show )
{
  if ( show == true )
  {
    BuildList();

    if ( m_CurrentSelection > m_Deck.Count() )
      SelectCard( 0 );
    else
      SelectCard( m_CurrentSelection );

    // problem when board switching, the text boxes lock until you select something
    // there's another copy of this in the Update() function
    // the next two lines are a pretty hack <+fix this+>
    m_TextFront->SetEditable( true );
    m_TextBack->SetEditable( true );
  }

  return wxPanel::Show( show );
} // ----- end of method StudyBoardPanel::Show -----

/**************************************************************************
Class: StudyBoardPanel
Method: OnSaveCard
Description: This handles savecard events.
**************************************************************************/
void StudyBoardPanel::OnSaveCard ( wxCommandEvent &event )
{
  size_t pos= m_ListBox->GetSelection();

  if ( pos == (size_t)wxNOT_FOUND )
    wxMessageBox( _("No card selected"),
                  _("No Card"), wxOK | wxICON_EXCLAMATION, this );
  else if ( m_TextFront->IsEmpty() || m_TextBack->IsEmpty() )
    wxMessageBox( _("Please enter a front and a back"),
                  _("Incomplete Card"), wxOK | wxICON_EXCLAMATION, this );
  else
  {
    UpdateCardEntry( pos );
    SaveCard( pos );
    SelectCard( pos );
    UpdateButtons();
  }
} // ----- end of method StudyBoardPanel::OnSaveCard -----

/**************************************************************************
Class: StudyBoardPanel
Method: OnDelCard
Description: This handles deleting of cards.
**************************************************************************/
void StudyBoardPanel::OnDelCard ( wxCommandEvent &event )
{
  size_t pos= m_ListBox->GetSelection();

  if ( pos != m_Deck.Count() )
  {
    DelCard( pos );
    UpdateButtons();

    // if last card is deleted and there are others,
    // select previous card, not "+ New Card"
    if ( pos == m_Deck.Count() && pos > 0 )
      --pos;

    SelectCard( pos );
  }
  else
    wxMessageBox( _("No deletable card selected"),
                  _("No Card"), wxOK | wxICON_EXCLAMATION, this );
} // ----- end of method StudyBoardPanel::OnDelCard -----

/**************************************************************************
Class: StudyBoardPanel
Method: OnHideList
Description: Hide the list panel.
**************************************************************************/
void StudyBoardPanel::OnHideList ( wxCommandEvent &event )
{
  if ( m_ListBox->IsShown() )
    m_ListBox->Hide();
  else
    m_ListBox->Show();

  Layout();
} // ----- end of method StudyBoardPanel::OnHideList -----

/**************************************************************************
Class: StudyBoardPanel
Method: OnSelect
Description: Handle selection in the list box.
**************************************************************************/
void StudyBoardPanel::OnSelect ( wxCommandEvent &event )
{
  bool isSelected= event.IsSelection();
  m_TextFront->SetEditable( isSelected );
  m_TextFront->SetEditable( isSelected );

  size_t newSelection= event.GetSelection();
  int result= ConfirmSwitchCard();

  if ( result == wxCANCEL )
    m_ListBox->SetSelection( m_CurrentSelection );
  else
    SelectCard( newSelection );
} // ----- end of method StudyBoardPanel::OnSelect -----

/**************************************************************************
Class: StudyBoardPanel
Method: OnDoubleClick
Description: On double click, save the current card.
**************************************************************************/
void StudyBoardPanel::OnDoubleClick ( wxCommandEvent &event )
{
  if ( (size_t) event.GetSelection() == m_CurrentSelection )
  {
    if ( m_TextFront->IsModified() || m_TextBack->IsModified() )
    {
      SaveCard( event.GetSelection() );
      BuildList();
      Update();
      SelectCard( event.GetSelection() );
    }
    m_TextFront->SetFocus();
  }
  else
  {
    size_t previous= m_CurrentSelection;

    OnSelect( event );

    if ( previous != m_CurrentSelection )
      m_TextFront->SetFocus();
  }
} // ----- end of method StudyBoardPanel::OnDoubleClick -----

/**************************************************************************
Class: StudyBoardPanel
Method: OnNext
Description: Go to the next card.
**************************************************************************/
void StudyBoardPanel::OnNext ( wxCommandEvent &event )
{
  size_t newSelection= m_CurrentSelection + 1;
  if ( newSelection >= m_Deck.Count() )
    newSelection= 0;

  if ( ConfirmSwitchCard() )
    SelectCard( newSelection );

  m_TextFront->SetFocus();
} // ----- end of method StudyBoardPanel::OnNext -----

/**************************************************************************
Class: StudyBoardPanel
Method: OnPrev
Description: Go to the next card.
**************************************************************************/
void StudyBoardPanel::OnPrev ( wxCommandEvent &event )
{
  size_t newSelection= m_CurrentSelection;
  if ( newSelection == 0 )
    newSelection= m_Deck.Count() - 1;
  else
    --newSelection;

  if ( ConfirmSwitchCard() )
    SelectCard( newSelection );

  m_TextFront->SetFocus();
} // ----- end of method StudyBoardPanel::OnPrev -----

/**************************************************************************
Class: StudyBoardPanel
Method: OnSort
Description: Sort the deck.
**************************************************************************/
void StudyBoardPanel::OnSort ( wxCommandEvent &event )
{
  int result= ConfirmSwitchCard();

  if ( result != wxCANCEL )
  {
    m_Deck.Sort();
    BuildList();
    SelectCard( 0 );
  }

  m_ListBox->SetFocus();
} // ----- end of method StudyBoardPanel::OnSort -----

/**************************************************************************
Class: StudyBoardPanel
Method: OnShuffle
Description: Shuffle the deck.
**************************************************************************/
void StudyBoardPanel::OnShuffle ( wxCommandEvent &event )
{
  int result= ConfirmSwitchCard();

  if ( result != wxCANCEL )
  {
    m_Deck.Shuffle();
    BuildList();
    SelectCard( 0 );
  }

  m_ListBox->SetFocus();
} // ----- end of method StudyBoardPanel::OnShuffle -----

